{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Erkennung von Ziffern\n", "\n", "Das Erkennen von Ziffern hat in dem Bereich Maschinelles Lernen Tradition.\n", "Hiervon ausgehend kann mit Schrifterkennung gearbeitet werden, um z. B. automatisch Briefe der Adresse nach zu sortieren.\n", "\n", "In diesem Jupyter Notebook werden die Ziffern klassifziert, die mit scikit-learn bereits mit ausgeliefert werden." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from sklearn import datasets\n", "from sklearn.ensemble import RandomForestClassifier\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.metrics import ConfusionMatrixDisplay\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zunächst wird der Datensatz mit den Ziffern geladen." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "digits = datasets.load_digits()\n", "print(digits.DESCR)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Darstellung einer Ziffer\n", "\n", "Nun wird exemplarisch die 0te Ziffer des Datensatzes dargestellt." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "digits.images[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Die Pixel werden anscheinend als Fließkommazahlen zwischen 0 und 16 dargestellt, wobei die Nachkommastellen nicht genutzt werden.\n", "Eine Ziffer wird durch eine $8 \\times 8$-Matrix repräsentiert.\n", "\n", "Schauen wir uns mal das allererste Bild an." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.imshow(digits.images[0], cmap=\"gray\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Es handelt sich scheinbar um eine 0, wobei aus der Matrix eine 0 als schwarz und eine 16 als weiß dargestellt wird.\n", "Die Zahlen repräsentieren Intensitätswerte." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wir als Menschen erkennen, dass es sich um eine Null handelt, aber ist dies auch richtig im Datensatz hinterlegt?\n", "Beim Überwachten Lernen brauchen wir für die Automatisierung bereits erkannte Ziffern." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Eine Möglichkeit, Abbildungen von Ziffern zu analysieren, ist, einfach jeden Pixel wie ein eigenes Attribut (also eine eigene Spalte in einer Tabelle) zu behandeln.\n", "Beim Aufruf von `flatten` werden die Reihen der Matrix aneinandergehängt, vgl. die [numpy-Dokumentation](https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.ndarray.flatten.html).\n", "Dabei erhält man eine eindimensionale Liste." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "flattened = digits.images[0].flatten()\n", "flattened" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hier sind nun einfach alle Pixel aneinander gehängt worden.\n", "So nimmt ein Bild nur eine Zeile ein." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(16, .2))\n", "ax.imshow(np.expand_dims(flattened, axis=0), cmap=\"gray\", aspect=\"auto\")\n", "plt.yticks([]) # verstecke Y-Achse\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vorverarbeitung der Bilder" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Die eindimensionale Repräsentation von Bildern ist notwendig, weil die bislang vorgestellten Klassifizierungs-Algorithmen nur einfache Attribute (quasi Spalten) verstehen - die räumliche Nähe der Pixel kann von ihnen nicht ausgewertet werden.\n", "Bei diesem Vorverarbeitungs-Schritt geht die Information über die räumliche Nähe im 2d-Bild offensichtlicherweise verloren." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "images = [image.flatten() for image in digits.images]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nun liegt die Eingabe so vor, dass damit wie bislang auch verfahren werden kann.\n", "Die Kategorie ist in `digits.target` vermerkt.\n", "Also `digits.images[22]` ist das Bild und in `digits.target[22]` steht dann, dass es sich bei dem Bild um eine Zwei handelt." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X_train, X_test, y_train, y_test = train_test_split(\n", " images, digits.target, test_size=0.33, random_state=42\n", ")\n", "\n", "clf = RandomForestClassifier(\n", " n_estimators=10,\n", " max_depth=7,\n", " max_features=\"sqrt\",\n", " class_weight=None,\n", " random_state=10\n", ")\n", "clf.fit(X_train, y_train)\n", "accuracy = clf.score(X_test, y_test)\n", "print(f\"Die erreichte Test-Genauigkeit: {accuracy:.02%}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nun lasssen wir uns eine Konfusionsmatrix anzeigen.\n", "Damit kann man sehen, welche Ziffern mit welchen anderen Ziffern am häufigsten verwechselt worden sind." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ConfusionMatrixDisplay.from_estimator(clf, X_test, y_test, cmap=\"BuPu\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Eigentlich ist die Vorhersage relativ gut: \n", "Die tatsächliche und die vorhergesagte Ziffer stimmen ganz gut überein.\n", "Man sieht nur, dass Fünfen und Achten häufiger für Neunen gehalten worden sind (in der Mitte rechts bzw. unten)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zur Erinnering: Mit dem Fragezeichen-Operator lässt sich die Dokumentation zu einem beliebigen Objekt, wie z. B. der Klasse `RandomForestClassifier` anzeigen.\n", "In Python sind auch Klassen Objekte." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "?RandomForestClassifier" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aufgabe\n", "\n", "Verändern Sie einen beliebigen Parameter des Random Forests und führen Sie den Code erneut aus.\n", "Tragen Sie die Parameter wie unten vorgegeben in die Liste ein.\n", "Falls Sie sich fragen, was die einzelnen Parameter des Random Forests bedeuten, verwenden Sie den Fragezeichen-Operator (siehe unten).\n", "Hat sich nach der Veränderung der Wert verbessert oder verschlechtert?\n", "Wiederholen Sie den Vorgang insgesamt fünfmal." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Ihre Antwort:*\n", "\n", "**Versuch 1**\n", "```python\n", "clf = RandomForestClassifier(\n", " n_estimators=10,\n", " max_depth=7,\n", " max_features=\"sqrt\",\n", " class_weight=None\n", ")\n", "# --> Test-Genauigkeit: 94,95 %\n", "```\n", "\n", "**Versuch 2**\n", "```python\n", "clf = ...\n", "# --> Test-Genauigkeit: ... %\n", "```\n", "\n", "**Versuch 3**\n", "```python\n", "clf = ...\n", "# --> Test-Genauigkeit: ... %\n", "```\n", "\n", "**Versuch 4**\n", "```python\n", "clf = ...\n", "# --> Test-Genauigkeit: ... %\n", "```\n", "\n", "**Versuch 5**\n", "```python\n", "clf = ...\n", "# --> Test-Genauigkeit: ... %\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ein weiterer Klassifizierer neben den Entscheidungsbäumen und dem Random Forest ist der k-Nächsten-Nachbarn-Algorithmus.\n", "Hier werden die Eingabedaten als Vektoren betrachtet und die euklidische Distanz zwischen diesen Eingabedaten berechnet.\n", "Es gibt keine Regeln, wann welcher Algorithmus besser funktioniert.\n", "Beim Maschinellen Lernen hilft nur Ausprobieren!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ein weiterer Klassifizierer" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.neighbors import KNeighborsClassifier\n", "neigh = KNeighborsClassifier(n_neighbors=3)\n", "neigh.fit(X_train, y_train)\n", "accuracy = neigh.score(X_test, y_test)\n", "print(f\"Die erreichte Test-Genauigkeit: {accuracy:.02%}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Die Literatur zur Erkennung von Ziffern ist sehr umfangreich, ein Startpunkt ist bspw. die Webseite http://yann.lecun.com/exdb/mnist/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\"Creative     Dieses Werk von Marvin Kastner ist lizenziert unter einer Creative Commons Namensnennung 4.0 International Lizenz." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.1" }, "nbsphinx": { "execute": "always" }, "toc-autonumbering": true, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }